home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / window.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  26KB  |  1,194 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. #include "vim.h"
  10. #include "globals.h"
  11. #include "proto.h"
  12. #include "param.h"
  13.  
  14. static int win_comp_pos __ARGS((void));
  15. static void win_exchange __ARGS((long));
  16. static void win_rotate __ARGS((int, int));
  17. static void win_append __ARGS((WIN *, WIN *));
  18. static void win_remove __ARGS((WIN *));
  19.  
  20. static WIN        *prevwin = NULL;        /* previous window */
  21.  
  22. /*
  23.  * all CTRL-W window commands are handled here, called from normal().
  24.  */
  25.     void
  26. do_window(nchar, Prenum)
  27.     int        nchar;
  28.     long    Prenum;
  29. {
  30.     long    Prenum1;
  31.     WIN        *wp;
  32.     char_u    *ptr;
  33.  
  34.     if (Prenum == 0)
  35.         Prenum1 = 1;
  36.     else
  37.         Prenum1 = Prenum;
  38.  
  39.     switch (nchar)
  40.     {
  41. /* split current window in two parts */
  42.     case 'S':
  43.     case Ctrl('S'):
  44.     case 's':    VIsual.lnum = 0;        /* stop Visual mode */
  45.                 win_split(Prenum, TRUE);
  46.                 break;
  47.  
  48. /* open new window */
  49.     case Ctrl('N'):
  50.     case 'n':    VIsual.lnum = 0;                    /* stop Visual mode */
  51.                 stuffcharReadbuff(':');
  52.                 if (Prenum)
  53.                     stuffnumReadbuff(Prenum);        /* window height */
  54.                 stuffReadbuff((char_u *)"new\n");    /* it is cmdline.c */
  55.                 break;
  56.  
  57. /* quit current window */
  58.     case Ctrl('Q'):
  59.     case 'q':    VIsual.lnum = 0;        /* stop Visual mode */
  60.                 stuffReadbuff((char_u *)":quit\n");    /* it is cmdline.c */
  61.                 break;
  62.  
  63. /* close current window */
  64.     case Ctrl('C'):
  65.     case 'c':    VIsual.lnum = 0;        /* stop Visual mode */
  66.                 stuffReadbuff((char_u *)":close\n");    /* it is cmdline.c */
  67.                 break;
  68.  
  69. /* close all but current window */
  70.     case Ctrl('O'):
  71.     case 'o':    VIsual.lnum = 0;        /* stop Visual mode */
  72.                 stuffReadbuff((char_u *)":only\n");    /* it is cmdline.c */
  73.                 break;
  74.  
  75. /* cursor to next window */
  76.     case 'j':
  77.     case K_DARROW:
  78.     case Ctrl('J'):
  79.                 VIsual.lnum = 0;        /* stop Visual mode */
  80.                 for (wp = curwin; wp->w_next != NULL && Prenum1-- > 0;
  81.                                                             wp = wp->w_next)
  82.                     ;
  83.                 win_enter(wp, TRUE);
  84.                 cursupdate();
  85.                 break;
  86.  
  87. /* cursor to next window with wrap around */
  88.     case Ctrl('W'):
  89.     case 'w':
  90.                 VIsual.lnum = 0;        /* stop Visual mode */
  91.                 if (lastwin == firstwin)        /* just one window */
  92.                     beep();
  93.                 else
  94.                 {
  95.                     if (Prenum)                    /* go to specified window */
  96.                     {
  97.                         for (wp = firstwin; --Prenum > 0; )
  98.                         {
  99.                             if (wp->w_next == NULL)
  100.                                 break;
  101.                             else
  102.                                 wp = wp->w_next;
  103.                         }
  104.                     }
  105.                     else                        /* go to next window */
  106.                     {
  107.                         wp = curwin->w_next;
  108.                         if (wp == NULL)
  109.                             wp = firstwin;        /* wrap around */
  110.                     }
  111.                     win_enter(wp, TRUE);
  112.                     cursupdate();
  113.                 }
  114.                 break;
  115.  
  116. /* cursor to window above */
  117.     case 'k':
  118.     case K_UARROW:
  119.     case Ctrl('K'):
  120.                 VIsual.lnum = 0;        /* stop Visual mode */
  121.                 for (wp = curwin; wp->w_prev != NULL && Prenum1-- > 0;
  122.                                                             wp = wp->w_prev)
  123.                     ;
  124.                 win_enter(wp, TRUE);
  125.                 cursupdate();
  126.                 break;
  127.  
  128. /* cursor to last accessed (previous) window */
  129.     case 'p':
  130.     case Ctrl('P'):
  131.                 VIsual.lnum = 0;        /* stop Visual mode */
  132.                 if (prevwin == NULL)
  133.                     beep();
  134.                 else
  135.                 {
  136.                     win_enter(prevwin, TRUE);
  137.                     cursupdate();
  138.                 }
  139.                 break;
  140.  
  141. /* exchange current and next window */
  142.     case 'x':
  143.     case Ctrl('X'):
  144.                 win_exchange(Prenum);
  145.                 break;
  146.  
  147. /* rotate windows downwards */
  148.     case Ctrl('R'):
  149.     case 'r':    VIsual.lnum = 0;                    /* stop Visual mode */
  150.                 win_rotate(FALSE, (int)Prenum1);    /* downwards */
  151.                 break;
  152.  
  153. /* rotate windows upwards */
  154.     case 'R':    VIsual.lnum = 0;                    /* stop Visual mode */
  155.                 win_rotate(TRUE, (int)Prenum1);        /* upwards */
  156.                 break;
  157.  
  158. /* make all windows the same height */
  159.     case '=':    win_equal(NULL, TRUE);
  160.                 break;
  161.  
  162. /* increase current window height */
  163.     case '+':    win_setheight(curwin->w_height + (int)Prenum1);
  164.                 break;
  165.  
  166. /* decrease current window height */
  167.     case '-':    win_setheight(curwin->w_height - (int)Prenum1);
  168.                 break;
  169.  
  170. /* set current window height */
  171.     case Ctrl('_'):
  172.     case '_':    win_setheight(Prenum ? (int)Prenum : 9999);
  173.                 break;
  174.  
  175. /* jump to tag and split window if tag exists */
  176.     case ']':
  177.     case Ctrl(']'):
  178.                 VIsual.lnum = 0;        /* stop Visual mode */
  179.                 postponed_split = TRUE;
  180.                 stuffcharReadbuff(Ctrl(']'));
  181.                 break;
  182.  
  183. /* edit file name under cursor in a new window */
  184.     case 'f':
  185.     case Ctrl('F'):
  186.                 VIsual.lnum = 0;        /* stop Visual mode */
  187.                 ptr = file_name_at_cursor();
  188.                 if (ptr == NULL)
  189.                     beep();
  190.                 else
  191.                 {
  192.                     stuffReadbuff((char_u *) ":split ");
  193.                     stuffReadbuff(ptr);
  194.                     stuffReadbuff((char_u *) "\n");
  195.                     free(ptr);
  196.                 }
  197.                 break;
  198.  
  199.     default:    beep();
  200.                 break;
  201.     }
  202. }
  203.  
  204. /*
  205.  * split the current window, implements CTRL-W s and :split
  206.  *
  207.  * new_height is the height for the new window, 0 to make half of current height
  208.  * redraw is TRUE when redraw now
  209.  *
  210.  * return FAIL for failure, OK otherwise
  211.  */
  212.     int
  213. win_split(new_height, redraw)
  214.     long    new_height;
  215.     int        redraw;
  216. {
  217.     WIN            *wp;
  218.     linenr_t    lnum;
  219.     int            h;
  220.     int            i;
  221.     int            need_status;
  222.     int            do_equal = (p_ea && new_height == 0);
  223.     int            needed;
  224.     int            available;
  225.     
  226.         /* add a status line when p_ls == 1 and splitting the first window */
  227.     if (lastwin == firstwin && p_ls == 1 && curwin->w_status_height == 0)
  228.         need_status = STATUS_HEIGHT;
  229.     else
  230.         need_status = 0;
  231.  
  232. /*
  233.  * check if we are able to split the current window and compute its height
  234.  */
  235.     available = curwin->w_height;
  236.      needed = 2 * MIN_ROWS + STATUS_HEIGHT + need_status;
  237.     if (p_ea)
  238.     {
  239.         for (wp = firstwin; wp != NULL; wp = wp->w_next)
  240.             if (wp != curwin)
  241.             {
  242.                 available += wp->w_height;
  243.                 needed += MIN_ROWS;
  244.             }
  245.     }
  246.      if (available < needed)
  247.     {
  248.         EMSG(e_noroom);
  249.         return FAIL;
  250.     }
  251.     if (need_status)
  252.     {
  253.         curwin->w_status_height = STATUS_HEIGHT;
  254.         curwin->w_height -= STATUS_HEIGHT;
  255.     }
  256.     if (new_height == 0)
  257.         new_height = curwin->w_height / 2;
  258.  
  259.     if (new_height > curwin->w_height - MIN_ROWS - STATUS_HEIGHT)
  260.         new_height = curwin->w_height - MIN_ROWS - STATUS_HEIGHT;
  261.  
  262.     if (new_height < MIN_ROWS)
  263.         new_height = MIN_ROWS;
  264.  
  265.         /* if it doesn't fit in the current window, need win_equal() */
  266.     if (curwin->w_height - new_height - STATUS_HEIGHT < MIN_ROWS)
  267.         do_equal = TRUE;
  268. /*
  269.  * allocate new window structure and link it in the window list
  270.  */
  271.     if (p_sb)        /* new window below current one */
  272.         wp = win_alloc(curwin);
  273.     else
  274.         wp = win_alloc(curwin->w_prev);
  275.     if (wp == NULL)
  276.         return FAIL;
  277. /*
  278.  * compute the new screen positions
  279.  */
  280.     wp->w_height = new_height;
  281.     win_comp_scroll(wp);
  282.     curwin->w_height -= new_height + STATUS_HEIGHT;
  283.     win_comp_scroll(curwin);
  284.     if (p_sb)        /* new window below current one */
  285.     {
  286.         wp->w_winpos = curwin->w_winpos + curwin->w_height + STATUS_HEIGHT;
  287.         wp->w_status_height = curwin->w_status_height;
  288.         curwin->w_status_height = STATUS_HEIGHT;
  289.     }
  290.     else            /* new window above current one */
  291.     {
  292.         wp->w_winpos = curwin->w_winpos;
  293.         wp->w_status_height = STATUS_HEIGHT;
  294.         curwin->w_winpos = wp->w_winpos + wp->w_height + STATUS_HEIGHT;
  295.     }
  296. /*
  297.  * make the contents of the new window the same as the current one
  298.  */
  299.     wp->w_buffer = curbuf;
  300.     curbuf->b_nwindows++;
  301.     wp->w_cursor = curwin->w_cursor;
  302.     wp->w_row = curwin->w_row;
  303.     wp->w_col = curwin->w_col;
  304.     wp->w_virtcol = curwin->w_virtcol;
  305.     wp->w_curswant = curwin->w_curswant;
  306.     wp->w_set_curswant = curwin->w_set_curswant;
  307.     wp->w_empty_rows = curwin->w_empty_rows;
  308.     wp->w_leftcol = curwin->w_leftcol;
  309.     wp->w_pcmark = curwin->w_pcmark;
  310.     wp->w_prev_pcmark = curwin->w_prev_pcmark;
  311.  
  312.     wp->w_arg_idx = curwin->w_arg_idx;
  313.     /*
  314.      * copy tagstack and options from existing window
  315.      */
  316.     for (i = 0; i < curwin->w_tagstacklen; i++)
  317.     {
  318.         wp->w_tagstack[i].fmark = curwin->w_tagstack[i].fmark;
  319.         wp->w_tagstack[i].tagname = strsave(curwin->w_tagstack[i].tagname);
  320.     }
  321.     wp->w_tagstackidx = curwin->w_tagstackidx;
  322.     wp->w_tagstacklen = curwin->w_tagstacklen;
  323.     win_copy_options(curwin, wp);
  324. /*
  325.  * Both windows need redrawing
  326.  */
  327.      wp->w_redr_type = NOT_VALID;
  328.     wp->w_redr_status = TRUE;
  329.      curwin->w_redr_type = NOT_VALID;
  330.     curwin->w_redr_status = TRUE;
  331. /*
  332.  * Cursor is put in middle of window in both windows
  333.  */
  334.     if (wp->w_height < curwin->w_height)    /* use smallest of two heights */
  335.         h = wp->w_height;
  336.     else
  337.         h = curwin->w_height;
  338.     h >>= 1;
  339.     for (lnum = wp->w_cursor.lnum; lnum > 1; --lnum)
  340.     {
  341.         h -= plines(lnum);
  342.         if (h <= 0)
  343.             break;
  344.     }
  345.     wp->w_topline = lnum;
  346.     curwin->w_topline = lnum;
  347. /*
  348.  * make the new window the current window and redraw
  349.  */
  350.     if (do_equal)
  351.         win_equal(wp, FALSE);
  352.      win_enter(wp, FALSE);
  353.     if (redraw)
  354.         updateScreen(NOT_VALID);
  355.     return OK;
  356. }
  357.  
  358. /*
  359.  * make 'count' windows on the screen
  360.  * return actual number of windows on the screen
  361.  * called when there is just one window, filling the whole screen.
  362.  */
  363.     int
  364. make_windows(count)
  365.     int        count;
  366. {
  367.     int        maxcount;
  368.     int        todo;
  369.     int        p_sb_save;
  370.  
  371. /*
  372.  * each window needs at least MIN_ROWS lines and a status line
  373.  */
  374.     maxcount = (curwin->w_height + curwin->w_status_height) /
  375.                                             (MIN_ROWS + STATUS_HEIGHT);
  376.     if (count > maxcount)
  377.         count = maxcount;
  378.  
  379.     /*
  380.      * add status line now, otherwise first window will be too big
  381.      */
  382.     if ((p_ls == 2 || (count > 1 && p_ls == 1)) && curwin->w_status_height == 0)
  383.     {
  384.         curwin->w_status_height = STATUS_HEIGHT;
  385.         curwin->w_height -= STATUS_HEIGHT;
  386.     }
  387.  
  388. /*
  389.  * set 'splitbelow' off for a moment, don't what that now
  390.  */
  391.     p_sb_save = p_sb;
  392.     p_sb = FALSE;
  393.         /* todo is number of windows left to create */
  394.     for (todo = count - 1; todo > 0; --todo)
  395.         if (win_split((long)(curwin->w_height - (curwin->w_height - todo
  396.                 * STATUS_HEIGHT) / (todo + 1) - STATUS_HEIGHT), FALSE) == FAIL)
  397.             break;
  398.     p_sb = p_sb_save;
  399.  
  400.         /* return actual number of windows */
  401.     return (count - todo);
  402. }
  403.  
  404. /*
  405.  * Exchange current and next window
  406.  */
  407.     static void
  408. win_exchange(Prenum)
  409.     long        Prenum;
  410. {
  411.     WIN        *wp;
  412.     WIN        *wp2;
  413.     int        temp;
  414.  
  415.     if (lastwin == firstwin)        /* just one window */
  416.     {
  417.         beep();
  418.         return;
  419.     }
  420.  
  421. /*
  422.  * find window to exchange with
  423.  */
  424.     if (Prenum)
  425.     {
  426.         wp = firstwin;
  427.         while (wp != NULL && --Prenum > 0)
  428.             wp = wp->w_next;
  429.     }
  430.     else if (curwin->w_next != NULL)    /* Swap with next */
  431.         wp = curwin->w_next;
  432.     else    /* Swap last window with previous */
  433.         wp = curwin->w_prev;
  434.  
  435.     if (wp == curwin || wp == NULL)
  436.         return;
  437.  
  438. /*
  439.  * 1. remove curwin from the list. Remember after which window it was in wp2
  440.  * 2. insert curwin before wp in the list
  441.  * if wp != wp2
  442.  *    3. remove wp from the list
  443.  *    4. insert wp after wp2
  444.  * 5. exchange the status line height
  445.  */
  446.     wp2 = curwin->w_prev;
  447.     win_remove(curwin);
  448.     win_append(wp->w_prev, curwin);
  449.     if (wp != wp2)
  450.     {
  451.         win_remove(wp);
  452.         win_append(wp2, wp);
  453.     }
  454.     temp = curwin->w_status_height;
  455.     curwin->w_status_height = wp->w_status_height;
  456.     wp->w_status_height = temp;
  457.  
  458.     win_comp_pos();                /* recompute window positions */
  459.  
  460.     win_enter(wp, TRUE);
  461.     cursupdate();
  462.     updateScreen(CLEAR);
  463. }
  464.  
  465. /*
  466.  * rotate windows: if upwards TRUE the second window becomes the first one
  467.  *                   if upwards FALSE the first window becomes the second one
  468.  */
  469.     static void
  470. win_rotate(upwards, count)
  471.     int        upwards;
  472.     int        count;
  473. {
  474.     WIN             *wp;
  475.     int             height;
  476.  
  477.     if (firstwin == lastwin)            /* nothing to do */
  478.     {
  479.         beep();
  480.         return;
  481.     }
  482.     while (count--)
  483.     {
  484.         if (upwards)            /* first window becomes last window */
  485.         {
  486.             wp = firstwin;
  487.             win_remove(wp);
  488.             win_append(lastwin, wp);
  489.             wp = lastwin->w_prev;            /* previously last window */
  490.         }
  491.         else                    /* last window becomes first window */
  492.         {
  493.             wp = lastwin;
  494.             win_remove(lastwin);
  495.             win_append(NULL, wp);
  496.             wp = firstwin;                    /* previously last window */
  497.         }
  498.             /* exchange status height of old and new last window */
  499.         height = lastwin->w_status_height;
  500.         lastwin->w_status_height = wp->w_status_height;
  501.         wp->w_status_height = height;
  502.  
  503.             /* recompute w_winpos for all windows */
  504.         (void) win_comp_pos();
  505.     }
  506.  
  507.     cursupdate();
  508.     updateScreen(CLEAR);
  509. }
  510.  
  511. /*
  512.  * make all windows the same height
  513.  */
  514.     void
  515. win_equal(next_curwin, redraw)
  516.     WIN        *next_curwin;            /* pointer to current window to be */
  517.     int        redraw;
  518. {
  519.     int        total;
  520.     int        less;
  521.     int        wincount;
  522.     int        winpos;
  523.     int        temp;
  524.     WIN        *wp;
  525.     int        new_height;
  526.  
  527. /*
  528.  * count the number of lines available
  529.  */
  530.     total = 0;
  531.     wincount = 0;
  532.     for (wp = firstwin; wp; wp = wp->w_next)
  533.     {
  534.         total += wp->w_height - MIN_ROWS;
  535.         wincount++;
  536.     }
  537.  
  538. /*
  539.  * if next_curwin given and 'winheight' set, make next_curwin p_wh lines
  540.  */
  541.     if (next_curwin != NULL && p_wh)
  542.     {
  543.         if (p_wh - MIN_ROWS > total)    /* all lines go to current window */
  544.             less = total;
  545.         else
  546.         {
  547.             less = p_wh - MIN_ROWS - total / wincount;
  548.             if (less < 0)
  549.                 less = 0;
  550.         }
  551.     }
  552.     else
  553.         less = 0;
  554.         
  555.  
  556. /*
  557.  * spread the available lines over the windows
  558.  */
  559.     winpos = 0;
  560.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  561.     {
  562.         if (wp == next_curwin && less)
  563.         {
  564.             less = 0;
  565.             temp = p_wh - MIN_ROWS;
  566.             if (temp > total)
  567.                 temp = total;
  568.         }
  569.         else
  570.             temp = (total - less + (wincount >> 1)) / wincount;
  571.         new_height = MIN_ROWS + temp;
  572.         if (wp->w_winpos != winpos || wp->w_height != new_height)
  573.         {
  574.             wp->w_redr_type = NOT_VALID;
  575.             wp->w_redr_status = TRUE;
  576.         }
  577.         wp->w_winpos = winpos;
  578.         wp->w_height = new_height;
  579.         win_comp_scroll(wp);
  580.         total -= temp;
  581.         --wincount;
  582.         winpos += wp->w_height + wp->w_status_height;
  583.     }
  584.     if (redraw)
  585.     {
  586.         cursupdate();
  587.         updateScreen(CLEAR);
  588.     }
  589. }
  590.  
  591. /*
  592.  * close current window
  593.  * If "free_buf" is TRUE related buffer may be freed.
  594.  *
  595.  * called by :quit, :close, :xit, :wq and findtag()
  596.  */
  597.     void
  598. close_window(free_buf)
  599.     int        free_buf;
  600. {
  601.     WIN     *wp;
  602.  
  603.     if (lastwin == firstwin)
  604.     {
  605.         EMSG("Cannot close last window");
  606.         return;
  607.     }
  608.  
  609. /*
  610.  * Close the link to the buffer.
  611.  */
  612.     close_buffer(curbuf, free_buf, FALSE);
  613.  
  614. /*
  615.  * Remove the window.
  616.  */
  617.     if (curwin->w_prev == NULL)        /* freed space goes to next window */
  618.     {
  619.         wp = curwin->w_next;
  620.         wp->w_winpos = curwin->w_winpos;
  621.     }
  622.     else                            /* freed space goes to previous window */
  623.         wp = curwin->w_prev;
  624.     wp->w_height += curwin->w_height + curwin->w_status_height;
  625.  
  626.     win_free(curwin);
  627.     curwin = NULL;
  628.     if (p_ea)
  629.         win_equal(wp, FALSE);
  630.     win_enter(wp, FALSE);
  631.     /*
  632.      * if last window has status line now and we don't want one,
  633.      * remove the status line
  634.      */
  635.     if (lastwin->w_status_height &&
  636.                         (p_ls == 0 || (p_ls == 1 && firstwin == lastwin)))
  637.     {
  638.         lastwin->w_height += lastwin->w_status_height;
  639.         lastwin->w_status_height = 0;
  640.         win_comp_scroll(lastwin);
  641.     }
  642.     win_comp_scroll(curwin);
  643.     updateScreen(NOT_VALID);
  644. }
  645.  
  646. /*
  647.  * close all windows except current one
  648.  * buffers in the windows become hidden
  649.  *
  650.  * called by :only and do_arg_all();
  651.  */
  652.     void
  653. close_others(message)
  654.     int        message;
  655. {
  656.     WIN     *wp;
  657.     WIN     *nextwp;
  658.  
  659.     if (lastwin == firstwin)
  660.     {
  661.         if (message)
  662.             EMSG("Already only one window");
  663.         return;
  664.     }
  665.  
  666.     for (wp = firstwin; wp != NULL; wp = nextwp)
  667.     {
  668.         nextwp = wp->w_next;
  669.         if (wp == curwin)                /* don't close current window */
  670.             continue;
  671.     /*
  672.      * Close the link to the buffer.
  673.      */
  674.         close_buffer(wp->w_buffer, FALSE, FALSE);
  675.  
  676.     /*
  677.      * Remove the window. All lines go to current window.
  678.      */
  679.         curwin->w_height += wp->w_height + wp->w_status_height;
  680.  
  681.         win_free(wp);
  682.     }
  683.     /*
  684.      * if current window has status line and we don't want one,
  685.      * remove the status line
  686.      */
  687.     if (curwin->w_status_height && p_ls != 2)
  688.     {
  689.         curwin->w_height += curwin->w_status_height;
  690.         curwin->w_status_height = 0;
  691.     }
  692.     curwin->w_winpos = 0;            /* put current window at top of the screen */
  693.     win_comp_scroll(curwin);
  694.     if (message)
  695.         updateScreen(NOT_VALID);
  696. }
  697.  
  698. /*
  699.  * init the cursor in the window
  700.  *
  701.  * called when a new file is being edited
  702.  */
  703.     void
  704. win_init(wp)
  705.     WIN        *wp;
  706. {
  707.     wp->w_redr_type = NOT_VALID;
  708.     wp->w_cursor.lnum = 1;
  709.     wp->w_curswant = wp->w_cursor.col = 0;
  710.     wp->w_pcmark.lnum = 1;        /* pcmark not cleared but set to line 1 */
  711.     wp->w_pcmark.col = 0;
  712.     wp->w_prev_pcmark.lnum = 0;
  713.     wp->w_prev_pcmark.col = 0;
  714.     wp->w_topline = 1;
  715.     wp->w_botline = 2;
  716. }
  717.  
  718. /*
  719.  * make window wp the current window
  720.  */
  721.     void
  722. win_enter(wp, undo_sync)
  723.     WIN        *wp;
  724.     int        undo_sync;
  725. {
  726.     if (wp == curwin)            /* nothing to do */
  727.         return;
  728.  
  729.         /* sync undo before leaving the current buffer */
  730.     if (undo_sync && curbuf != wp->w_buffer)
  731.         u_sync();
  732.     if (curwin != NULL)
  733.         prevwin = curwin;        /* remember for CTRL-W p */
  734.     curwin = wp;
  735.     curbuf = wp->w_buffer;
  736.     maketitle();
  737.             /* set window height to desired minimal value */
  738.     if (p_wh && curwin->w_height < p_wh)
  739.         win_setheight((int)p_wh);
  740. }
  741.  
  742. /*
  743.  * allocate a window structure and link it in the window list
  744.  */
  745.     WIN *
  746. win_alloc(after)
  747.     WIN        *after;
  748. {
  749.     WIN        *new;
  750.  
  751. /*
  752.  * allocate window structure and linesizes arrays
  753.  */
  754.     new = (WIN *)alloc((unsigned)sizeof(WIN));
  755.     if (new)
  756.     {
  757. /*
  758.  * most stucture members have to be zero
  759.  */
  760.          memset((char *)new, 0, sizeof(WIN));
  761. /*
  762.  * link the window in the window list
  763.  */
  764.         win_append(after, new);
  765.  
  766.         win_alloc_lsize(new);
  767.  
  768.         /* position the display and the cursor at the top of the file. */
  769.         new->w_topline = 1;
  770.         new->w_cursor.lnum = 1;
  771.     }
  772.     return new;
  773. }
  774.  
  775. /*
  776.  * remove window 'wp' from the window list and free the structure
  777.  */
  778.     void
  779. win_free(wp)
  780.     WIN        *wp;
  781. {
  782.     if (prevwin == wp)
  783.         prevwin = NULL;
  784.     win_free_lsize(wp);
  785.     win_remove(wp);
  786.     free(wp);
  787. }
  788.  
  789.     static void
  790. win_append(after, wp)
  791.     WIN        *after, *wp;
  792. {
  793.     WIN     *before;
  794.  
  795.     if (after == NULL)        /* after NULL is in front of the first */
  796.         before = firstwin;
  797.     else
  798.         before = after->w_next;
  799.  
  800.     wp->w_next = before;
  801.     wp->w_prev = after;
  802.     if (after == NULL)
  803.         firstwin = wp;
  804.     else
  805.         after->w_next = wp;
  806.     if (before == NULL)
  807.         lastwin = wp;
  808.     else
  809.         before->w_prev = wp;
  810. }
  811.  
  812. /*
  813.  * remove window from the window list
  814.  */
  815.     static void
  816. win_remove(wp)
  817.     WIN        *wp;
  818. {
  819.     if (wp->w_prev)
  820.         wp->w_prev->w_next = wp->w_next;
  821.     else
  822.         firstwin = wp->w_next;
  823.     if (wp->w_next)
  824.         wp->w_next->w_prev = wp->w_prev;
  825.     else
  826.         lastwin = wp->w_prev;
  827. }
  828.  
  829. /*
  830.  * allocate lsize arrays for a window
  831.  * return FAIL for failure, OK for success
  832.  */
  833.     int
  834. win_alloc_lsize(wp)
  835.     WIN        *wp;
  836. {
  837.     wp->w_lsize_valid = 0;
  838.     wp->w_lsize_lnum = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
  839.     wp->w_lsize = (char_u *)malloc((size_t) Rows);
  840.     if (wp->w_lsize_lnum == NULL || wp->w_lsize == NULL)
  841.     {
  842.         win_free_lsize(wp);        /* one of the two may have worked */
  843.         wp->w_lsize_lnum = NULL;
  844.         wp->w_lsize = NULL;
  845.         return FAIL;
  846.     }
  847.     return OK;
  848. }
  849.  
  850. /*
  851.  * free lsize arrays for a window
  852.  */
  853.      void
  854. win_free_lsize(wp)
  855.     WIN        *wp;
  856. {
  857.     free(wp->w_lsize_lnum);
  858.     free(wp->w_lsize);
  859. }
  860.  
  861. /*
  862.  * call this fuction whenever Rows changes value
  863.  */
  864.     void
  865. screen_new_rows()
  866. {
  867.     WIN        *wp;
  868.     int        extra_lines;
  869.  
  870.     if (firstwin == NULL)        /* not initialized yet */
  871.         return;
  872. /*
  873.  * the number of extra lines is the difference between the position where
  874.  * the command line should be and where it is now
  875.  */
  876.     compute_cmdrow();
  877.     extra_lines = Rows - p_ch - cmdline_row;
  878.     if (extra_lines < 0)                        /* reduce windows height */
  879.     {
  880.         for (wp = lastwin; wp; wp = wp->w_prev)
  881.         {
  882.             if (wp->w_height - MIN_ROWS < -extra_lines)
  883.             {
  884.                 extra_lines += wp->w_height - MIN_ROWS;
  885.                 wp->w_height = MIN_ROWS;
  886.                 win_comp_scroll(wp);
  887.             }
  888.             else
  889.             {
  890.                 wp->w_height += extra_lines;
  891.                 win_comp_scroll(wp);
  892.                 break;
  893.             }
  894.         }
  895.         (void)win_comp_pos();                    /* compute w_winpos */
  896.     }
  897.     else if (extra_lines > 0)                    /* increase height of last window */
  898.     {
  899.         lastwin->w_height += extra_lines;
  900.         win_comp_scroll(lastwin);
  901.     }
  902.  
  903.     compute_cmdrow();
  904. }
  905.  
  906. /*
  907.  * update the w_winpos field for all windows
  908.  * returns the row just after the last window
  909.  */
  910.     static int
  911. win_comp_pos()
  912. {
  913.     WIN        *wp;
  914.     int        row;
  915.  
  916.     row = 0;
  917.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  918.     {
  919.         if (wp->w_winpos != row)        /* if position changes, redraw */
  920.         {
  921.             wp->w_winpos = row;
  922.             wp->w_redr_type = NOT_VALID;
  923.             wp->w_redr_status = TRUE;
  924.         }
  925.         row += wp->w_height + wp->w_status_height;
  926.     }
  927.     return row;
  928. }
  929.  
  930. /*
  931.  * set current window height
  932.  */
  933.     void
  934. win_setheight(height)
  935.     int        height;
  936. {
  937.     WIN        *wp;
  938.     int        room;                /* total number of lines available */
  939.     int        take;                /* number of lines taken from other windows */
  940.     int        room_cmdline;        /* lines available from cmdline */
  941.     int        row;
  942.  
  943.     if (height < MIN_ROWS)        /* need at least some lines */
  944.         height = MIN_ROWS;
  945. /*
  946.  * compute the room we have from all the windows
  947.  */
  948.     room = MIN_ROWS;            /* count the MIN_ROWS for the current window */
  949.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  950.         room += wp->w_height - MIN_ROWS;
  951. /*
  952.  * compute the room available from the command line
  953.  */
  954.     room_cmdline = Rows - p_ch - cmdline_row;
  955. /*
  956.  * limit new height to the room available
  957.  */
  958.     if (height > room + room_cmdline)            /* can't make it that large */
  959.         height = room + room_cmdline;            /* use all available room */
  960. /*
  961.  * compute the number of lines we will take from the windows (can be negative)
  962.  */
  963.     take = height - curwin->w_height;
  964.     if (take == 0)                                /* no change, nothing to do */
  965.         return;
  966.  
  967.     if (take > 0)
  968.     {
  969.         take -= room_cmdline;                    /* use lines from cmdline first */
  970.         if (take < 0)
  971.             take = 0;
  972.     }
  973. /*
  974.  * set the current window to the new height
  975.  */
  976.     curwin->w_height = height;
  977.     win_comp_scroll(curwin);
  978. /*
  979.  * take lines from the windows below the current window
  980.  */
  981.     for (wp = curwin->w_next; wp != NULL && take != 0; wp = wp->w_next)
  982.     {
  983.         if (wp->w_height - take < MIN_ROWS)
  984.         {
  985.             take -= wp->w_height - MIN_ROWS;
  986.             wp->w_height = MIN_ROWS;
  987.         }
  988.         else
  989.         {
  990.             wp->w_height -= take;
  991.             take = 0;
  992.         }
  993.         win_comp_scroll(wp);                /* recompute p_scroll */
  994.         wp->w_redr_type = NOT_VALID;        /* need to redraw this window */
  995.         wp->w_redr_status = TRUE;
  996.     }
  997. /*
  998.  * take lines from the windows above the current window
  999.  */
  1000.     for (wp = curwin->w_prev; wp != NULL && take != 0; wp = wp->w_prev)
  1001.     {
  1002.         if (wp->w_height - take < MIN_ROWS)
  1003.         {
  1004.             take -= wp->w_height - MIN_ROWS;
  1005.             wp->w_height = MIN_ROWS;
  1006.         }
  1007.         else
  1008.         {
  1009.             wp->w_height -= take;
  1010.             take = 0;
  1011.         }
  1012.         win_comp_scroll(wp);                /* recompute p_scroll */
  1013.         wp->w_redr_type = NOT_VALID;        /* need to redraw this window */
  1014.         wp->w_redr_status = TRUE;
  1015.     }
  1016.  
  1017. /* recompute the window positions */
  1018.     row = win_comp_pos();
  1019.  
  1020. /*
  1021.  * If there is extra space created between the last window and the command line,
  1022.  * clear it.
  1023.  */
  1024.      screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ');
  1025.     cmdline_row = row;
  1026.  
  1027.     updateScreen(NOT_VALID);
  1028. }
  1029.  
  1030.     void
  1031. win_comp_scroll(wp)
  1032.     WIN        *wp;
  1033. {
  1034.     wp->w_p_scroll = (wp->w_height >> 1);
  1035.     if (wp->w_p_scroll == 0)
  1036.         wp->w_p_scroll = 1;
  1037. }
  1038.  
  1039. /*
  1040.  * command_height: called whenever p_ch has been changed
  1041.  */
  1042.     void
  1043. command_height()
  1044. {
  1045.     int        current;
  1046.  
  1047.     current = Rows - cmdline_row;
  1048.     if (current > p_ch)                /* p_ch got smaller */
  1049.         lastwin->w_height += current - p_ch;
  1050.     else                            /* p_ch got bigger */
  1051.     {
  1052.         if (lastwin->w_height - (p_ch - current) < MIN_ROWS)
  1053.         {
  1054.             emsg(e_noroom);
  1055.             p_ch = lastwin->w_height - MIN_ROWS + current;
  1056.         }
  1057.         lastwin->w_height -= p_ch - current;
  1058.                                     /* clear the lines added to cmdline */
  1059.         screen_fill((int)(Rows - p_ch), (int)Rows, 0, (int)Columns, ' ', ' ');
  1060.     }
  1061.     win_comp_scroll(lastwin);
  1062.     cmdline_row = Rows - p_ch;
  1063.     lastwin->w_redr_type = NOT_VALID;
  1064.     lastwin->w_redr_status = TRUE;
  1065.     redraw_cmdline = TRUE;
  1066. }
  1067.  
  1068.     void
  1069. last_status()
  1070. {
  1071.     if (lastwin->w_status_height)
  1072.     {
  1073.                     /* remove status line */
  1074.         if (p_ls == 0 || (p_ls == 1 && firstwin == lastwin))
  1075.         {
  1076.             lastwin->w_status_height = 0;
  1077.             lastwin->w_height++;
  1078.             win_comp_scroll(lastwin);
  1079.             lastwin->w_redr_status = TRUE;
  1080.         }
  1081.     }
  1082.     else
  1083.     {
  1084.                     /* add status line */
  1085.         if (p_ls == 2 || (p_ls == 1 && firstwin != lastwin))
  1086.         {
  1087.             if (lastwin->w_height <= MIN_ROWS)        /* can't do it */
  1088.                 emsg(e_noroom);
  1089.             else
  1090.             {
  1091.                 lastwin->w_status_height = 1;
  1092.                 lastwin->w_height--;
  1093.                 win_comp_scroll(lastwin);
  1094.                 lastwin->w_redr_status = TRUE;
  1095.             }
  1096.         }
  1097.     }
  1098. }
  1099.  
  1100. /*
  1101.  * file_name_at_cursor()
  1102.  *
  1103.  * Return the name of the file under (or to the right of) the cursor.  The
  1104.  * p_path variable is searched if the file name does not start with '/'.
  1105.  * The string returned has been alloc'ed and should be freed by the caller.
  1106.  * NULL is returned if the file name or file is not found.
  1107.  */
  1108.     char_u *
  1109. file_name_at_cursor()
  1110. {
  1111.     char_u    *ptr;
  1112.     char_u    *dir;
  1113.     char_u    *file_name;
  1114.     char_u    save_char;
  1115.     int        col;
  1116.     int        len;
  1117.  
  1118.         /* characters in a file name besides alfa-num */
  1119. #ifdef UNIX
  1120.     char_u    *file_chars = (char_u *)"/.-_+,~$";
  1121. #endif
  1122. #ifdef AMIGA
  1123.     char_u    *file_chars = (char_u *)"/.-_+,$:";
  1124. #endif
  1125. #ifdef MSDOS
  1126.     char_u    *file_chars = (char_u *)"/.-_+,$\\:";
  1127. #endif
  1128.  
  1129.     ptr = ml_get(curwin->w_cursor.lnum);
  1130.     col = curwin->w_cursor.col;
  1131.  
  1132.         /* search forward for what could be the start of a file name */
  1133.     while (!isalnum((char) ptr[col]) && STRCHR(file_chars, ptr[col]) == NULL)
  1134.         ++col;
  1135.     if (ptr[col] == NUL)            /* nothing found */
  1136.         return NULL;
  1137.  
  1138.         /* search backward for char that cannot be in a file name */
  1139.     while (col >= 0 &&
  1140.       (isalnum((char) ptr[col]) || STRCHR(file_chars, ptr[col]) != NULL))
  1141.         --col;
  1142.     ptr += col + 1;
  1143.     col = 0;
  1144.  
  1145.         /* search forward for a char that cannot be in a file name */
  1146.     while (ptr[col] != NUL
  1147.       && (isalnum((char) ptr[col]) || STRCHR(file_chars, ptr[col]) != NULL))
  1148.         ++col;
  1149.  
  1150.         /* copy file name into NameBuff, expanding environment variables */
  1151.     save_char = ptr[col];
  1152.     ptr[col] = NUL;
  1153.     expand_env(ptr, NameBuff, MAXPATHL);
  1154.     ptr[col] = save_char;
  1155.  
  1156.     if (isFullName(NameBuff))            /* absolute path */
  1157.     {
  1158.         if ((file_name = strsave(NameBuff)) == NULL)
  1159.             return NULL;
  1160.         if (getperm(file_name) >= 0)
  1161.             return file_name;
  1162.     }
  1163.     else                            /* relative path, use 'path' option */
  1164.     {
  1165.         if ((file_name = alloc((int)(STRLEN(p_path) + STRLEN(NameBuff) + 2))) == NULL)
  1166.             return NULL;
  1167.         dir = p_path;
  1168.         for (;;)
  1169.         {
  1170.             skipspace(&dir);
  1171.             for (len = 0; dir[len] != NUL && dir[len] != ' '; len++)
  1172.                 ;
  1173.             if (len == 0)
  1174.                 break;
  1175.             if (len == 1 && dir[0] == '.')        /* current dir */
  1176.                 STRCPY(file_name, NameBuff);
  1177.             else
  1178.             {
  1179.                 STRNCPY(file_name, dir, (size_t)len);
  1180. #ifdef AMIGA            /* Amiga doesn't like c:/file */
  1181.                 if (file_name[len - 1] != ':')
  1182. #endif
  1183.                     file_name[len] = '/';
  1184.                 STRCPY(file_name + len + 1, NameBuff);
  1185.             }
  1186.             if (getperm(file_name) >= 0)
  1187.                 return file_name;
  1188.             dir += len;
  1189.         }
  1190.     }
  1191.     free(file_name);            /* file doesn't exist */
  1192.     return NULL;
  1193. }
  1194.